package middleware

import (
	"bytes"
	"encoding/json"
	"gitee.com/wuzheng0709/backend-gopkg/domain/entity"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/code"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/gin/log"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/jwt"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/toolfunc"
	"github.com/getsentry/sentry-go"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"io"
	"net"
	"net/http/httputil"
	"os"
	"runtime/debug"
	"strconv"
	"strings"
	"time"
)

func GinLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		c.Next()

		logger := log.TeamsLog

		latency := time.Since(start)
		logger.Info(path,
			zap.Int("status", c.Writer.Status()),
			zap.String("method", c.Request.Method),
			zap.String("path", path),
			zap.String("query", query),
			zap.String("ip", GetRealIp(c)),
			zap.String("user-agent", c.Request.UserAgent()),
			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.Duration("latency", latency),
		)
	}
}

const (
	USER_UID_KEY      = "UID" //页面UUID键名
	USER_REALNAME_KEY = "RealName"
	USER_USERNAME_KEY = "UserName"
	SUPER_ADMIN_ID    = "10" //超级管理员
	InterfaceTrack    = "interface"
)

type GetUserInfoFunc func(userId int64) (userName, realName string, Usertype int)

type SkipReqBodyLogByUrl func(string) bool

// fn 默认传输 nil
func GinLoggerV2(fn SkipReqBodyLogByUrl) gin.HandlerFunc {
	return func(c *gin.Context) {
		var latency time.Duration = -9999
		currentTime := c.Request.Header.Get("X-Current-Time")

		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		token := c.Request.Header.Get("token")
		logDb := entity.InterfaceTransferLog{}
		// TODO: 之后删除通过url过滤
		if skipReadReqBodyByContentType(c.Request.Header.Get("Content-Type")) {
			logDb.Param = "跳过读取（body）"
		} else {
			reqBodyByte, _ := io.ReadAll(c.Request.Body)
			c.Request.Body = io.NopCloser(bytes.NewBuffer(reqBodyByte))
			logDb.Param = string(reqBodyByte)
		}
		// 通过 url 过滤body
		if fn != nil && fn(path) {
			logDb.Param = "跳过读取（body）"
		} else {
			reqBodyByte, _ := io.ReadAll(c.Request.Body)
			c.Request.Body = io.NopCloser(bytes.NewBuffer(reqBodyByte))
			logDb.Param = string(reqBodyByte)
		}
		claims, _ := jwt.ParseTokenFromRedis(token)
		if claims == nil {
			logDb.UserID = 0
		} else {
			logDb.UserID = toolfunc.String2Int64(claims.UserId)
		}

		if logDb.UserID != 0 {
			c.Header("UserName", claims.UserName)
			c.Header("RealName", claims.RealName)
			c.Header("UserId", strconv.FormatInt(logDb.UserID, 10))
		}

		// 设置相关变量
		c.Set(USER_REALNAME_KEY, claims.RealName)
		c.Set(USER_USERNAME_KEY, claims.UserName)
		c.Set("ecmAccount", claims.UserName)
		if claims.UserType != "1" {
			c.Set("ecmAccount", "hpdiFile")
		}

		if path != "/" {

			// 生成的日志信息
			logger := zap.L()
			if currentTime != "" {

				layout := "Monday, 02-Jan-2006 15:04:05 MST"
				loc, _ := time.LoadLocation("Asia/Shanghai") // 或者使用其他方式获取时区
				parsedTime, _ := time.Parse(layout, currentTime)
				parsedTime = parsedTime.In(loc)
				latency = time.Since(parsedTime)
			}

			logger.Info(path,
				zap.Int("status", c.Writer.Status()),
				zap.String("method", c.Request.Method),
				zap.String("path", path),
				zap.String("query", query),
				zap.String("ip", GetRealIp(c)),
				zap.String("user-agent", c.Request.UserAgent()),
				zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
				zap.Duration("latency", latency),
			)

			// 写入数据库中
			logDb.Method = c.Request.Method
			logDb.Path = path
			logDb.Query = query
			logDb.IP = GetRealIp(c)
			logDb.Errors = c.Errors.ByType(gin.ErrorTypePrivate).String()
			logDb.Latency = int(latency.Milliseconds())
			logDb.Username = claims.UserName
			logDb.RealName = claims.RealName
			logDb.LogType = InterfaceTrack
			logDb.Header = c.Request.Header
			logDbByte, _ := json.Marshal(logDb)
			log.Info(string(logDbByte))
		}
		resWrrite := PrintResponse(c)
		c.Next()
		if path != "/" {
			logDb.Errors = c.Errors.ByType(gin.ErrorTypePrivate).String()
			logDb.Status = c.Writer.Status()
			logDb.ResBody = resWrrite.b.String()
			logDbByte, _ := json.Marshal(logDb)
			log.Info(string(logDbByte))
		}
	}
}

// 判断 header 中的 Content-Type 值, ture: 存在
func skipReadReqBodyByContentType(contentType string) bool {
	switch {
	case strings.Contains(contentType, "multipart/form-data"):
		return true
	default:
		return false
	}
	return false
}

// 自定义一个结构体，实现 gin.ResponseWriter interface
type responseWriter struct {
	gin.ResponseWriter
	b *bytes.Buffer
}

// 重写 Write([]byte) (int, error) 方法
func (w responseWriter) Write(b []byte) (int, error) {
	//向一个bytes.buffer中写一份数据来为获取body使用
	w.b.Write(b)
	//完成gin.Context.Writer.Write()原有功能
	return w.ResponseWriter.Write(b)
}

// 重写 response
func PrintResponse(c *gin.Context) responseWriter {
	writer := responseWriter{
		c.Writer,
		bytes.NewBuffer([]byte{}),
	}
	c.Writer = writer
	return writer
}

//func Logger(logger *zap.Logger) gin.HandlerFunc {
//	return func(c *gin.Context) {
//		start := time.Now()
//		path := c.Request.URL.Path
//		query := c.Request.URL.RawQuery
//		c.Next()
//
//		if logger == nil {
//			logger = zap.L()
//		}
//
//		latency := time.Since(start)
//		logger.Info(path,
//			zap.Int("status", c.Writer.Status()),
//			zap.String("method", c.Request.Method),
//			zap.String("path", path),
//			zap.String("query", query),
//			zap.String("ip", c.Request.Header.Get("X-Real-IP")),
//			zap.String("user-agent", c.Request.UserAgent()),
//			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
//			zap.Duration("latency", latency),
//		)
//		if c.Writer.Status() >= 400 {
//			sentry.CaptureMessage("请求失败" + path + string(c.Writer.Status()) + query + c.Errors.ByType(gin.ErrorTypePrivate).String() + string(latency))
//		}
//		if latency >= 2000 {
//			sentry.CaptureMessage("慢请求,待优化" + path + string(c.Writer.Status()) + query + c.Errors.ByType(gin.ErrorTypePrivate).String() + string(latency))
//		}
//	}
//}

func Recovery(logger *zap.Logger, stack bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				if logger == nil {
					logger = zap.L()
				}
				log.Info("============")
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}

				httpRequest, _ := httputil.DumpRequest(c.Request, false)
				log.Info("============2")
				if brokenPipe {
					logger.Error(c.Request.URL.Path,
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
					// If the connection is dead, we can't write a status to it.
					c.Error(err.(error)) // nolint: errcheck
					c.Abort()
					return
				}
				sentry.CaptureMessage(err.(error).Error() + string(httpRequest) + string(debug.Stack()))
				if stack {
					logger.Error("[Recovery from panic]",
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
						zap.String("stack", string(debug.Stack())),
					)
				} else {
					logger.Error("[Recovery from panic]",
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)

				}
				log.Error(err.(error).Error())
				log.Error(string(httpRequest))
				log.Error(string(debug.Stack()))
				c.JSON(500, gin.H{"code": code.Crash_Error, "msg": "发生错误,已自动上报"})
				c.Abort()
				return
			}
		}()
		c.Next()
	}
}

func GetRealIp(c *gin.Context) string {
	if c.Request.Header.Get("X-Real-Forwarded-For") != "" {
		return c.Request.Header.Get("X-Real-Forwarded-For")
	} else if c.Request.Header.Get("Remote-Host") != "" {
		return c.Request.Header.Get("Remote-Host")
	}
	return c.Request.Header.Get("X-Real-Ip")
}
